package com.jzo2o.es.config;

import cn.hutool.core.date.DatePattern;
import com.jzo2o.es.core.ElasticSearchTemplate;
import com.jzo2o.es.core.impl.ElasticSearchTemplateImpl;
import com.jzo2o.es.properties.EsProperties;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
// @Configuration：声明这是一个配置类，Spring Boot 会自动扫描并加载其中的 Bean 定义
@Configuration
// @EnableConfigurationProperties：启用配置属性绑定，将配置文件中的属性映射到 EsProperties 类
@EnableConfigurationProperties(EsProperties.class)
public class EsConfiguration {

    // 创建静态的 ObjectMapper 实例，用于 JSON 序列化和反序列化配置
    // static 静态代码块在类加载时执行，确保 ObjectMapper 在 Bean 创建前完成初始化
    private static final ObjectMapper MAPPER = new ObjectMapper();

    static {
        // @配置1：设置属性命名策略为蛇形命名法（snake_case）
        // 例如：userName -> user_name
        MAPPER.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);

        // @配置2：设置序列化时忽略 null 值
        // 当字段值为 null 时，不会包含在 JSON 输出中
        MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);

        // @配置3：注册 Java 8 时间模块，支持 LocalDateTime 等类型的序列化
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        // 定义日期时间格式器，使用标准的日期时间格式：yyyy-MM-dd HH:mm:ss
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN);

        // @配置4：添加 LocalDateTime 的序列化器
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(dateTimeFormatter));
        // @配置5：添加 LocalDateTime 的反序列化器
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(dateTimeFormatter));

        // 将时间模块注册到 ObjectMapper
        MAPPER.registerModule(javaTimeModule);
    }

    // @Bean：声明这是一个 Spring Bean，方法名作为 Bean 的名称
    // 参数 esProperties 由 Spring 自动注入（通过 @EnableConfigurationProperties 启用）
    @Bean
    public ElasticsearchClient esClient(EsProperties esProperties) {
        // @步骤1：创建 RestClient，用于与 Elasticsearch 建立 HTTP 连接
        // 构建 RestClient，指定 Elasticsearch        的主机和端口
        RestClient restClient = RestClient
                .builder(new HttpHost(esProperties.getHost(), esProperties.getPort()))
                .build();

        // @步骤2：创建 JacksonJsonpMapper，使用前面配置的 ObjectMapper
        // 这个 mapper 用于 Elasticsearch 客户端的数据序列化/反序列化
        JacksonJsonpMapper jacksonJsonpMapper = new JacksonJsonpMapper(MAPPER);

        // @步骤3：创建传输层，将 RestClient 和 JSON 映射器组合
        ElasticsearchTransport transport = new RestClientTransport(restClient, jacksonJsonpMapper);

        // @步骤4：创建并返回 ElasticsearchClient 实例
        return new ElasticsearchClient(transport);
    }

    // @Bean：声明 ElasticSearchTemplate 的 Bean
    // 参数 elasticsearchClient 由 Spring 自动注入（上面定义的 esClient Bean）
    @Bean
    public ElasticSearchTemplate template(ElasticsearchClient elasticsearchClient) {
        // 创建自定义的 ElasticSearchTemplate 实现类实例
        // 包装了 ElasticsearchClient，提供更便捷的操作方法
        return new ElasticSearchTemplateImpl(elasticsearchClient);
    }
}
